/********************************************************************************
  * @file    programconfig.cpp
  * @author  Lun Li
  * @version V2.2.0
  * @date    2022.7.8
  * @brief   This file provides all the ProgramConfig functions.
  ******************************************************************************/

#include "programconfig.h"
#include "signalconnection.h"

ProgramConfig::ProgramConfig(const QString &fileName, QSettings::Format format, QObject *parent)
    : QSettings(fileName, format, parent)
{
    beginGroup("Editor");
    m_font.fromString(value("Font", QFont("Courier New",11,50).toString()).toString());
    m_tabWidth = value("TabWidth", 8).toInt();
    m_keywordColor = QColor::fromRgba(value("KeywordColor", QColor(Qt::blue).rgba()).toUInt());
    m_quotationColor = QColor::fromRgba(value("QuotationColor", QColor(Qt::darkGray).rgba()).toUInt());
    m_numberColor = QColor::fromRgba(value("NumberColor", QColor(Qt::darkCyan).rgba()).toUInt());
    m_commentColor = QColor::fromRgba(value("CommentColor", QColor(Qt::darkGreen).rgba()).toUInt());
    m_systemFunctionColor = QColor::fromRgba(value("SystemFunctionColor", QColor(Qt::darkMagenta).rgba()).toUInt());
    endGroup();

    beginGroup("Tool");
    m_compileToolPath = value("CompileToolPath", QDir::currentPath() + "/iverilog/bin").toString();
    m_waveToolPath = value("WaveToolPath", QDir::currentPath() + "/gtkwave/bin").toString();
    m_isLogEnabled = value("EnableLog", true).toBool();
    m_isLoadHierarchyEnabled = value("EnableLoadHierarchy", true).toBool();
    m_iverilogTimeout = value("IverilogTimeout", 30000).toInt();
    m_vvpTimeout = value("VvpTimeout", 30000).toInt();
    endGroup();

    beginGroup("Iverilog");
    m_commandFilePath = value("CommandFilePath", QString()).toString();
    QStringList debugFlagsStr = value("debugFlags", QString()).toString().split(',');
    for (const QString &str : debugFlagsStr) {
        if (!str.isEmpty()) {
            m_debugFlags << DebugFlag(str.toInt());
        }
    }
    QStringList generationFlagsStr = value("GenerationFlags", QString()).toString().split(',');
    for (const QString &str : generationFlagsStr) {
        if (!str.isEmpty()) {
            m_generationFlags << GenerationFlag(str.toInt());
        }
    }
    m_ifIgnoreMissingModule = value("IgnoreModules", false).toBool();
    beginGroup("VPIModuleDir");
    for (QString &key : childKeys()) {
        m_VPIModuleDirs << value(key).toString();
    }
    endGroup();
    beginGroup("LibraryFile");
    for (QString &key : childKeys()) {
        m_libraryFilePaths << value(key).toString();
    }
    endGroup();
    beginGroup("ContributionModePath");
    for (QString &key : childKeys()) {
        m_contributionModePaths << value(key).toString();
    }
    endGroup();
    beginGroup("VPIModule");
    for (QString &key : childKeys()) {
        m_VPIModules << value(key).toString();
    }
    endGroup();
    m_outputFileName = value("OutputFileName", "*.out").toString();
    m_ifActivateSynth = value("ActivateSynthesis", false).toBool();
    m_ifTreatAsUnit = value("TreatSourceAsUnit", false).toBool();
    m_ifBeVerbose = value("BeVerbose", false).toBool();
    m_ifPrintVersion = value("PrintVersion", false).toBool();
    QStringList warningFlagsStr = value("WarningFlags", QString()).toString().split(',');
    for (const QString &str : warningFlagsStr) {
        if (!str.isEmpty()) {
            m_warningFlags << WarningFlag(str.toInt());
        }
    }
    beginGroup("LibraryDir");
    for (QString &key : childKeys()) {
        m_libraryDirs << value(key).toString();
    }
    endGroup();
    beginGroup("Suffix");
    for (QString &key : childKeys()) {
        m_suffixes << value(key).toString();
    }
    endGroup();
    m_preOutputFileName = value("PreOutputFileName", QString()).toString();
    beginGroup("Macro");
    for (QString &key : childKeys()) {
        m_macros << value(key).toString();
    }
    endGroup();
    beginGroup("Parameter");
    for (QString &key : childKeys()) {
        m_parameters << value(key).toString();
    }
    endGroup();
    beginGroup("TopModule");
    for (QString &key : childKeys()) {
        m_topModules << value(key).toString();
    }
    endGroup();
    m_timingFlag = TimingFlag(value("TimingFlag", timing_typ).toInt());
    m_vvpExtra = value("VVPExtra", QString()).toString();
    endGroup();

    beginGroup("General");
    m_lastOpenPath = value("LastOpenPath", QString()).toString();
    m_languageIndex = value("Language", 0).toInt();
    endGroup();

    extern SignalConnection globalSignal;
    connect(this, &ProgramConfig::editorConfigChanged, &globalSignal, &SignalConnection::editorConfigChanged);
}

const QFont &ProgramConfig::font() const
{
    return m_font;
}

void ProgramConfig::setFont(const QFont &newFont)
{
    m_font = newFont;
    beginGroup("Editor");
    setValue("Font", newFont);
    endGroup();
    emit editorConfigChanged();
}

int ProgramConfig::tabWidth() const
{
    return m_tabWidth;
}

void ProgramConfig::setTabWidth(int newWidth)
{
    m_tabWidth = newWidth;
    beginGroup("Editor");
    setValue("TabWidth", newWidth);
    endGroup();
    emit editorConfigChanged();
}

const QColor &ProgramConfig::keywordColor() const
{
    return m_keywordColor;
}

void ProgramConfig::setKeywordColor(const QColor &newColor)
{
    m_keywordColor = newColor;
    beginGroup("Editor");
    setValue("KeywordColor", newColor.rgba());
    endGroup();
    emit editorConfigChanged();
}

const QColor &ProgramConfig::quotationColor() const
{
    return m_quotationColor;
}

void ProgramConfig::setQuotationColor(const QColor &newColor)
{
    m_quotationColor = newColor;
    beginGroup("Editor");
    setValue("QuotationColor", newColor.rgba());
    endGroup();
    emit editorConfigChanged();
}

const QColor &ProgramConfig::numberColor() const
{
    return m_numberColor;
}

void ProgramConfig::setNumberColor(const QColor &newColor)
{
    m_numberColor = newColor;
    beginGroup("Editor");
    setValue("NumberColor", newColor.rgba());
    endGroup();
    emit editorConfigChanged();
}

const QColor &ProgramConfig::commentColor() const
{
    return m_commentColor;
}

void ProgramConfig::setCommentColor(const QColor &newColor)
{
    m_commentColor = newColor;
    beginGroup("Editor");
    setValue("CommentColor", newColor.rgba());
    endGroup();
    emit editorConfigChanged();
}

const QColor &ProgramConfig::systemFunctionColor() const
{
    return m_systemFunctionColor;
}

void ProgramConfig::setSystemFunctionColor(const QColor &newColor)
{
    m_systemFunctionColor = newColor;
    beginGroup("Editor");
    setValue("SystemFunctionColor", newColor.rgba());
    endGroup();
    emit editorConfigChanged();
}

const QString &ProgramConfig::compileToolPath() const
{
    return m_compileToolPath;
}

void ProgramConfig::setCompileToolPath(const QString &newPath)
{
    m_compileToolPath = newPath;
    beginGroup("Tool");
    setValue("CompileToolPath", newPath);
    endGroup();
    emit toolConfigChanged();
}

const QString &ProgramConfig::waveToolPath() const
{
    return m_waveToolPath;
}

void ProgramConfig::setWaveToolPath(const QString &newPath)
{
    m_waveToolPath = newPath;
    beginGroup("Tool");
    setValue("WaveToolPath", newPath);
    endGroup();
    emit toolConfigChanged();
}

bool ProgramConfig::isLogEnabled() const
{
    return m_isLogEnabled;
}

void ProgramConfig::setIsLogEnabled(bool newEnabled)
{
    m_isLogEnabled = newEnabled;
    beginGroup("Tool");
    setValue("EnableLog", newEnabled);
    endGroup();
    emit toolConfigChanged();
}

bool ProgramConfig::isLoadHierarchyEnabled() const
{
    return m_isLoadHierarchyEnabled;
}

void ProgramConfig::setIsLoadHierarchyEnabled(bool newEnabled)
{
    m_isLoadHierarchyEnabled = newEnabled;
    beginGroup("Tool");
    setValue("EnableLoadHierarchy", newEnabled);
    endGroup();
    emit toolConfigChanged();
}

qint32 ProgramConfig::iverilogTimeout() const
{
    return m_iverilogTimeout;
}

void ProgramConfig::setIverilogTimeout(qint32 newIverilogTimeout)
{
    m_iverilogTimeout = newIverilogTimeout;
    beginGroup("Tool");
    setValue("IverilogTimeout", newIverilogTimeout);
    endGroup();
    emit toolConfigChanged();
}

qint32 ProgramConfig::vvpTimeout() const
{
    return m_vvpTimeout;
}

void ProgramConfig::setVvpTimeout(qint32 newVvpTimeout)
{
    m_vvpTimeout = newVvpTimeout;
    beginGroup("Tool");
    setValue("VvpTimeout", newVvpTimeout);
    endGroup();
    emit toolConfigChanged();
}

const QString &ProgramConfig::commandFilePath() const
{
    return m_commandFilePath;
}

void ProgramConfig::setCommandFilePath(const QString &newPath)
{
    m_commandFilePath = newPath;
    beginGroup("Iverilog");
    setValue("CommandFilePath", newPath);
    endGroup();
    emit iverilogConfigChanged();
}

const QList<ProgramConfig::DebugFlag> &ProgramConfig::debugFlags() const
{
    return m_debugFlags;
}

void ProgramConfig::setdebugFlags(const QList<DebugFlag> &newFlags)
{
    m_debugFlags = newFlags;
    QStringList flagList;
    for (DebugFlag flag : newFlags) {
        flagList << QString::number(flag);
    }
    beginGroup("Iverilog");
    setValue("debugFlags", flagList.join(','));
    endGroup();
    emit iverilogConfigChanged();
}

const QList<ProgramConfig::GenerationFlag> &ProgramConfig::generationFlags() const
{
    return m_generationFlags;
}

void ProgramConfig::setGenerationFlags(const QList<GenerationFlag> &newFlags)
{
    m_generationFlags = newFlags;
    QStringList flagList;
    for (GenerationFlag flag : newFlags) {
        flagList << QString::number(flag);
    }
    beginGroup("Iverilog");
    setValue("GenerationFlags", flagList.join(','));
    endGroup();
    emit iverilogConfigChanged();
}

bool ProgramConfig::ifIgnoreMissingModule() const
{
    return m_ifIgnoreMissingModule;
}

void ProgramConfig::setIfIgnoreMissingModule(bool newIgnore)
{
    m_ifIgnoreMissingModule = newIgnore;
    beginGroup("Iverilog");
    setValue("IgnoreModules", newIgnore);
    endGroup();
    emit iverilogConfigChanged();
}

const QStringList &ProgramConfig::VPIModuleDirs() const
{
    return m_VPIModuleDirs;
}

void ProgramConfig::setVPIModuleDirs(const QStringList &newDirs)
{
    m_VPIModuleDirs = newDirs;
    int pathLen = newDirs.length();
    beginGroup("Iverilog");
    beginGroup("VPIModuleDir");
    remove("");
    for (int i = 0; i < pathLen; ++i) {
        setValue(QString::number(i + 1), newDirs.at(i));
    }
    endGroup();
    endGroup();
    emit iverilogConfigChanged();
}

const QStringList &ProgramConfig::libraryFilePaths() const
{
    return m_libraryFilePaths;
}

void ProgramConfig::setLibraryFilePaths(const QStringList &newPaths)
{
    m_libraryFilePaths = newPaths;
    int pathLen = newPaths.length();
    beginGroup("Iverilog");
    beginGroup("LibraryFile");
    remove("");
    for (int i = 0; i < pathLen; ++i) {
        setValue(QString::number(i + 1), newPaths.at(i));
    }
    endGroup();
    endGroup();
    emit iverilogConfigChanged();
}

const QStringList &ProgramConfig::contributionModePaths() const
{
    return m_contributionModePaths;
}

void ProgramConfig::setContributionModePaths(const QStringList &newModePaths)
{
    m_contributionModePaths = newModePaths;
    int pathLen = newModePaths.length();
    beginGroup("Iverilog");
    beginGroup("ContributionModePath");
    remove("");
    for (int i = 0; i < pathLen; ++i) {
        setValue(QString::number(i + 1), newModePaths.at(i));
    }
    endGroup();
    endGroup();
    emit iverilogConfigChanged();
}

const QStringList &ProgramConfig::VPIModules() const
{
    return m_VPIModules;
}

void ProgramConfig::setVPIModules(const QStringList &newModules)
{
    m_VPIModules = newModules;
    int pathLen = newModules.length();
    beginGroup("Iverilog");
    beginGroup("VPIModule");
    remove("");
    for (int i = 0; i < pathLen; ++i) {
        setValue(QString::number(i + 1), newModules.at(i));
    }
    endGroup();
    endGroup();
    emit iverilogConfigChanged();
}

const QString &ProgramConfig::outputFileName() const
{
    return m_outputFileName;
}

void ProgramConfig::setOutputFileName(const QString &newName)
{
    m_outputFileName = newName;
    beginGroup("Iverilog");
    setValue("OutputFileName", newName);
    endGroup();
    emit iverilogConfigChanged();
}

bool ProgramConfig::ifActivateSynth() const
{
    return m_ifActivateSynth;
}

void ProgramConfig::setIfActivateSynth(bool newActivate)
{
    m_ifActivateSynth = newActivate;
    beginGroup("Iverilog");
    setValue("ActivateSynthesis", newActivate);
    endGroup();
    emit iverilogConfigChanged();
}

bool ProgramConfig::ifTreatAsUnit() const
{
    return m_ifTreatAsUnit;
}

void ProgramConfig::setIfTreatAsUnit(bool newTreat)
{
    m_ifTreatAsUnit = newTreat;
    beginGroup("Iverilog");
    setValue("TreatSourceAsUnit", newTreat);
    endGroup();
    emit iverilogConfigChanged();
}

bool ProgramConfig::ifBeVerbose() const
{
    return m_ifBeVerbose;
}

void ProgramConfig::setIfBeVerbose(bool newVerbose)
{
    m_ifBeVerbose = newVerbose;
    beginGroup("Iverilog");
    setValue("BeVerbose", newVerbose);
    endGroup();
    emit iverilogConfigChanged();
}

bool ProgramConfig::ifPrintVersion() const
{
    return m_ifPrintVersion;
}

void ProgramConfig::setIfPrintVersion(bool newPrint)
{
    m_ifPrintVersion = newPrint;
    beginGroup("Iverilog");
    setValue("PrintVersion", newPrint);
    endGroup();
    emit iverilogConfigChanged();
}

const QList<ProgramConfig::WarningFlag> &ProgramConfig::warningFlags() const
{
    return m_warningFlags;
}

void ProgramConfig::setWarningFlags(const QList<WarningFlag> &newFlags)
{
    m_warningFlags = newFlags;
    QStringList flagList;
    for (WarningFlag flag : newFlags) {
        flagList << QString::number(flag);
    }
    beginGroup("Iverilog");
    setValue("WarningFlags", flagList.join(','));
    endGroup();
    emit iverilogConfigChanged();
}

const QStringList &ProgramConfig::libraryDirs() const
{
    return m_libraryDirs;
}

void ProgramConfig::setLibraryDirs(const QStringList &newDirs)
{
    m_libraryDirs = newDirs;
    int pathLen = newDirs.length();
    beginGroup("Iverilog");
    beginGroup("LibraryDir");
    remove("");
    for (int i = 0; i < pathLen; ++i) {
        setValue(QString::number(i + 1), newDirs.at(i));
    }
    endGroup();
    endGroup();
    emit iverilogConfigChanged();
}

const QStringList &ProgramConfig::suffixes() const
{
    return m_suffixes;
}

void ProgramConfig::setSuffixes(const QStringList &newSuffixes)
{
    m_suffixes = newSuffixes;
    int sufLen = newSuffixes.length();
    beginGroup("Iverilog");
    beginGroup("Suffix");
    remove("");
    for (int i = 0; i < sufLen; ++i) {
        setValue(QString::number(i + 1), newSuffixes.at(i));
    }
    endGroup();
    endGroup();
    emit iverilogConfigChanged();
}

const QString &ProgramConfig::preOutputFileName() const
{
    return m_preOutputFileName;
}

void ProgramConfig::setPreOutputFileName(const QString &newName)
{
    m_preOutputFileName = newName;
    beginGroup("Iverilog");
    setValue("PreOutputFileName", newName);
    endGroup();
    emit iverilogConfigChanged();
}

const QStringList &ProgramConfig::macros() const
{
    return m_macros;
}

void ProgramConfig::setMacros(const QStringList &newMacros)
{
    m_macros = newMacros;
    int macroLen = newMacros.length();
    beginGroup("Iverilog");
    beginGroup("Macro");
    remove("");
    for (int i = 0; i < macroLen; ++i) {
        setValue(QString::number(i + 1), newMacros.at(i));
    }
    endGroup();
    endGroup();
    emit iverilogConfigChanged();
}

const QStringList &ProgramConfig::parameters() const
{
    return m_parameters;
}

void ProgramConfig::setParameters(const QStringList &newParameters)
{
    m_parameters = newParameters;
    int paramLen = newParameters.length();
    beginGroup("Iverilog");
    beginGroup("Parameter");
    remove("");
    for (int i = 0; i < paramLen; ++i) {
        setValue(QString::number(i + 1), newParameters.at(i));
    }
    endGroup();
    endGroup();
    emit iverilogConfigChanged();
}

const QStringList &ProgramConfig::topModules() const
{
    return m_topModules;
}

void ProgramConfig::setTopModules(const QStringList &newTopModules)
{
    m_topModules = newTopModules;
    int modulesLen = newTopModules.length();
    beginGroup("Iverilog");
    beginGroup("TopModule");
    remove("");
    for (int i = 0; i < modulesLen; ++i) {
        setValue(QString::number(i + 1), newTopModules.at(i));
    }
    endGroup();
    endGroup();
    emit iverilogConfigChanged();
}

ProgramConfig::TimingFlag ProgramConfig::timingFlag() const
{
    return m_timingFlag;
}

void ProgramConfig::setTimingFlag(TimingFlag newFlag)
{
    m_timingFlag = newFlag;
    beginGroup("Iverilog");
    setValue("TimingFlag", newFlag);
    endGroup();
    emit iverilogConfigChanged();
}

const QString &ProgramConfig::vvpExtra() const
{
    return m_vvpExtra;
}

void ProgramConfig::setVvpExtra(const QString &newVvpExtra)
{
    m_vvpExtra = newVvpExtra;
    beginGroup("Iverilog");
    setValue("VVPExtra", newVvpExtra);
    endGroup();
    emit iverilogConfigChanged();
}

qint32 ProgramConfig::languageIndex() const
{
    return m_languageIndex;
}

void ProgramConfig::setLanguageIndex(qint32 newLanguageIndex)
{
    m_languageIndex = newLanguageIndex;
    beginGroup("General");
    setValue("Language", newLanguageIndex);
    endGroup();
    emit generalConfigChanged();
}

const QString &ProgramConfig::lastOpenPath() const
{
    return m_lastOpenPath;
}

void ProgramConfig::setLastOpenPath(const QString &newPath)
{
    m_lastOpenPath = newPath;
    beginGroup("General");
    setValue("LastOpenPath", newPath);
    endGroup();
    emit generalConfigChanged();
}

